use std::num::Wrapping;
use std::ops::{Add, Mul};
use std::usize;

pub trait Zero: Sized + Add<Self, Output = Self> {
  fn zero() -> Self;

  fn set_zero(&mut self) {
    *self = Zero::zero();
  }

  fn is_zero(&self) -> bool;
}

macro_rules! zero_impl {
  ($t:ty, $v:expr) => {
    impl Zero for $t {
      #[inline]
      fn zero() -> $t {
        $v
      }
      #[inline]
      fn is_zero(&self) -> bool {
        *self == $v
      }
    }
  };
}

zero_impl!(usize, 0);
zero_impl!(u8, 0);
zero_impl!(u16, 0);
zero_impl!(u32, 0);
zero_impl!(u64, 0);
zero_impl!(u128, 0);

zero_impl!(isize, 0);
zero_impl!(i8, 0);
zero_impl!(i16, 0);
zero_impl!(i32, 0);
zero_impl!(i64, 0);
zero_impl!(i128, 0);

zero_impl!(f32, 0.0);
zero_impl!(f64, 0.0);

impl<T: Zero> Zero for Wrapping<T>
where
  Wrapping<T>: Add<Output = Wrapping<T>>,
{
  fn is_zero(&self) -> bool {
    self.0.is_zero()
  }
  fn set_zero(&mut self) {
    self.0.set_zero();
  }
  fn zero() -> Self {
    Wrapping(T::zero())
  }
}

pub trait One: Sized + Mul<Self, Output = Self> {
  fn one() -> Self;

  fn set_one(&mut self) {
    *self = One::one();
  }

  fn is_one(&self) -> bool
  where
    Self: PartialEq,
  {
    *self == Self::one()
  }
}

macro_rules! one_impl {
  ($t:ty, $v:expr) => {
    impl One for $t {
      #[inline]
      fn one() -> $t {
        $v
      }
      #[inline]
      fn is_one(&self) -> bool {
        *self == $v
      }
    }
  };
}

one_impl!(usize, 1);
one_impl!(u8, 1);
one_impl!(u16, 1);
one_impl!(u32, 1);
one_impl!(u64, 1);
one_impl!(u128, 1);

one_impl!(isize, 1);
one_impl!(i8, 1);
one_impl!(i16, 1);
one_impl!(i32, 1);
one_impl!(i64, 1);
one_impl!(i128, 1);

one_impl!(f32, 1.0);
one_impl!(f64, 1.0);

impl<T: One> One for Wrapping<T>
where
  Wrapping<T>: Mul<Output = Wrapping<T>>,
{
  fn set_one(&mut self) {
    self.0.set_one();
  }

  fn one() -> Self {
    Wrapping(T::one())
  }
}

#[allow(dead_code)]
#[inline(always)]
pub fn zero<T: Zero>() -> T {
  Zero::zero()
}

#[allow(dead_code)]
#[inline(always)]
pub fn one<T: One>() -> T {
  One::one()
}
